#!/usr/bin/bash

# Copyright (C) George Makrydakis 2009-2011,2013
# Copyright (C) Con Kolivas 2011-2012,2016,2018,2021,2026

# A bash wrapper for Con Kolivas' excellent lrzip utility. For the time
# being, lrzip does not like pipes, so we had to do this. It is kind of
# self - documenting, spawned out of a test tube bash shell script.

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Rewrite argv tokens so non-compat lrzip gets options it understands:
#  -e PASS / -ePASS  ->  --encrypt=PASS  (optional_argument short form is fragile)
#  -1 .. -9          ->  -L N            (digits are compat/lrz only)
function lrztar_fix_lrzip_args() {
	local -n _src=$1
	local -n _dst=$2
	local _i _a _next
	_dst=()
	for ((_i=0; _i<${#_src[@]}; _i++)); do
		_a=${_src[_i]}
		case $_a in
		-[1-9])
			_dst+=(-L "${_a:1}")
			;;
		-e|--encrypt)
			_next=${_src[_i+1]-}
			if [[ -n $_next && $_next != -* ]]; then
				_dst+=("--encrypt=${_next}")
				((_i++))
			else
				_dst+=("$_a")
			fi
			;;
		-e?*)
			_dst+=("--encrypt=${_a#-e}")
			;;
		*)
			_dst+=("$_a")
			;;
		esac
	done
}

function lrztar_local() {
	local hv="\
lrztar GNU/bash wrapper script for lrzip and tar input/output over directories.
Copyright (C) George Makrydakis 2009-2011,2013
Copyright (C) Con Kolivas       2011-2012,2016,2018,2021,2026

Usage : lrztar [lrzip options] <directory>
Result: a lrzip tarball is produced.
Extras: when an lrzip tarball is used with -d, -O, it gets extracted:

 -h: will display this message.
 -d: <path1> will decompress a <path1> lrzip tarball to current directory.
 -O: <path2> will decompress a -d specified lrzip tarball to <path2> path.
 -f: will force overwrites.
 -t: test integrity of a lrzip tarball (lrzip -t).
 -i: show information about a lrzip tarball (lrzip -i).
 -V: show lrztar / lrzip version.
 -e <password>: encrypt with password (required form for this wrapper).
 -Q: very quiet (passed to lrzip).
 -C / -c: check integrity of files written on decompression.
 -K / -k: keep broken or damaged output files.

Notice:
 - The input argument is always last, all options and their arguments precede.
 - The -O flag is an option flag, goes before: (-O <somedir> <input arg>).
 - Put -f before -d when forcing extract: lrztar -f -d archive.tar.lrz
 - Remaining lrzip short options are accepted and passed through.
 - lrzuntar is equivalent to lrztar [options] -d <filename>.
 - This script exists because of how lrzip behaves.
 - Beware the -f flag, it stands for what it says...
 - Compression is tar|lrzip; -e must include the password on the command line
   (stdin is the tar stream). Example: lrztar -e secret mydir
"
	[[ $1 == "" ]] && {
		printf "lrztar: no arguments given\n"
		return 1
	}
	local p=("${@:1:$(($#-1))}") s="${!#}" vopt=("lrz") \
		v_w=0 v_S=0 v_D=0 v_p=0 v_q=0 v_Q=0 v_L=0 \
		v_n=0 v_l=0 v_b=0 v_g=0 v_z=0 v_U=0 \
		v_T=0 v_N=0 v_v=0 v_f=0 v_d=0 v_h=0 \
		v_H=0 v_c=0 v_C=0 v_k=0 v_K=0 v_o=0 v_O=0 v_m=0 \
		v_e=0 v_i=0 v_t=0 v_V=0 \
		v_1=0 v_2=0 v_3=0 v_4=0 v_5=0 v_6=0 v_7=0 v_8=0 v_9=0 \
		x= i="$(pwd)"
	tar --version &> /dev/null \
		|| { printf "lrztar: no tar in your path\n"; return 1; }
	lrzip --version &> /dev/null \
		|| { printf "lrztar: no lrzip in your path\n"; return 1; }
	lrzcat --version &> /dev/null \
		|| { printf "lrztar: no lrzcat in your path\n"; return 1; }

	# Short options mirrored from lrzip (non-compat). Ones that need an
	# argument keep a trailing colon.
	# -e requires a password argument: tar|lrzip shares stdin, so a TTY
	#   prompt on lrzip's stdin is impossible; -e PASS keeps getopts parsing
	#   the remaining flags correctly.
	# -d takes the archive path as its argument (lrztar convention).
	# -t/-i/-V are handled by this wrapper instead of being rejected.
	while getopts ":w:O:S:DqQL:nlbgzUm:TN:p:vfo:d:tVhHcCkKe:i123456789" x; do
		case $x in
		\?)
			printf "lrztar: invalid option: -%s\n" "$OPTARG"
			return 1
			;;
		:)
			printf "lrztar: option -%s requires an argument\n" "$OPTARG"
			return 1
			;;
		esac
		((v_$x=${#vopt[@]}))
		vopt[${#vopt[@]}]="$OPTARG"
	done

	[[ $(basename "$0") == lrzuntar ]] \
		&& { ((v_d=${#vopt[@]})); vopt[${#vopt[@]}]="$s"; }

	{ ! (($#)) || ((v_h)); } && {
		printf "%s\n" "$hv"
		return
	}

	((v_V)) && {
		printf "lrztar wrapper (bash) for lrzip directory archives\n"
		lrzip -V
		return
	}

	# -t / -i act on an existing .tar.lrz (or similar), no tar involved.
	((v_t || v_i)) && {
		local fixed=()
		[[ -e $s ]] || {
			printf "lrztar: file does not exist: %s\n" "$s"
			return 1
		}
		lrztar_fix_lrzip_args p fixed
		lrzip "${fixed[@]}" "$s"
		return $?
	}

	((v_d)) && {
		local fixed=()
		[[ -e ${vopt[v_d]} ]] || {
			printf "lrztar: file does not exist: %s\n" \
				"${vopt[v_d]}"
			return 1
		}
		i+="/${vopt[v_d]##*/}"
		i="${i%.tar.*}"
		if ((v_O)); then
			for x in ${!p[@]}; do
				[[ ${p[x]} == "-O" ]] && {
					p[x]=
					p[$((x+1))]=
					break
				}
			done
			i="${vopt[v_O]%/}"
			x="${s##*/}"
			if [[ -d "$i/${x%.tar.*}" ]] && ! ((v_f)); then
				printf "lrztar: %s exists, use -f.\n" \
					"$i/${x%.tar.*}"
				return 1
			fi
			if ! [[ -d $i ]]; then
				printf "lrztar: %s output path does not exist.\n" \
					"$i"
				return 1
			fi
		else
			i="./"
		fi
		# Drop -d ARCHIVE from args passed to lrzcat (archive is "$s").
		for x in ${!p[@]}; do
			if [[ ${p[x]} == "-d" ]]; then
				p[x]=
				# argument already captured in vopt; if next
				# token is the archive path, clear it too
				if [[ -n ${p[x+1]-} && ${p[x+1]} == "${vopt[v_d]}" ]]; then
					p[$((x+1))]=
				fi
				break
			fi
		done
		lrztar_fix_lrzip_args p fixed
		p=("${fixed[@]}")
		# strip empty slots from cleared -O/-d entries
		fixed=()
		for x in "${p[@]}"; do
			[[ -n $x ]] && fixed+=("$x")
		done
		p=("${fixed[@]}")
		[ ! -z "$s" ] && {
			lrzcat "${p[@]}" "$s" | tar x -C "$i"
			x=$?
		} || {
			lrzcat "${p[@]}" | tar x -C "$i"
			x=$?
		}
	} || {
		# Compress path: tar | lrzip. Normalize -e PASS / -N for non-compat lrzip.
		local fixed=()
		lrztar_fix_lrzip_args p fixed
		p=("${fixed[@]}")

		if ((v_o)); then
			! ((v_f)) && [[ -e ${vopt[$v_o]} ]] && {
				printf "lrztar: %s exists, use -f to overwrite.\n" \
					"${vopt[$v_o]}"
				return 1
			}
		else
			if ((v_O)); then
				if ! [[ -d ${vopt[v_O]} ]]; then
					printf "lrztar: %s output path does not exist.\n" \
						"${vopt[v_O]}"
					return 1
				fi
				for x in ${!p[@]}; do
					[[ ${p[x]} == "-O" ]] && {
						p[x]=
						i="${p[$((x+1))]%/}"
						p[$((x+1))]=
						s="${!#}"
						break
					}
				done
			fi
			s="${s%/}"
			p+=(-o "$i/${s##*/}.tar.${vopt[v_S]}")
		fi
		if ! ((v_o)); then
			! ((v_f)) && [[ -e $i/${s##*/}.tar.${vopt[v_S]} ]] && {
				printf "lrztar: %s exists, use -f to overwrite\n" \
					"$i/${s##*/}.tar.${vopt[v_S]}"
				return 1
			}
		fi
		tar c "$s" | lrzip "${p[@]}"
		x=$?
	}
	return $x
}

lrztar_local "${@}"
